/*******************************************************************************
 * Copyright (c) 2012, 2013 XML-Print Project.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     XML-Print Project - initial API and implementation
 ******************************************************************************/
package info.xmlprint.core.xml;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.util.Iterator;
import java.util.LinkedList;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Text;
import org.dom4j.io.SAXContentHandler;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

public class SpacingHandle {
	LinkedList<String> sp = new LinkedList<String>();
	LinkedList<String> sp2 = new LinkedList<String>();

	StringBuilder content = new StringBuilder();
	String filePath;

	public SpacingHandle(String filePath) {
		this.filePath = filePath;
		// long l1 = new Date().getTime();
		// changeWithDom();
		changeWithSAXParser();// schneller als modifyUseDom
		// long l2 = new Date().getTime() - l1;
		// System.out.println("Time:" + l2);
	}

	public void changeWithDom() {

		try {

			// FileInputStream fis = new FileInputStream(filePath);
			// InputStreamReader isr = new InputStreamReader(fis, "UTF-8");
			// BufferedReader br = new BufferedReader(isr);
			//
			// SAXReader reader = new SAXReader();
			// Document doc = reader.read(br);
			// Element root = doc.getRootElement();
			// nodeByNodes(root, root.attributeValue("letter-spacing"));
			//
			// FileOutputStream fos = new FileOutputStream(filePath);
			// OutputStreamWriter writer = new OutputStreamWriter(fos, "UTF-8");
			// XMLWriter xmlWriter = new XMLWriter(writer);
			// xmlWriter.write(doc);
			// xmlWriter.close();
			// writer.close();

			InputStream in = new FileInputStream(filePath);
			InputSource is = new InputSource(in);
			SAXReader reader = new SAXReader();
			Document doc = reader.read(is);
			Element root = doc.getRootElement();
			nodeByNodes(root, root.attributeValue("letter-spacing"));

			FileOutputStream fos = new FileOutputStream(filePath);
			OutputStreamWriter writer = new OutputStreamWriter(fos, "UTF-8");
			XMLWriter xmlWriter = new XMLWriter(writer);
			xmlWriter.write(doc);
			xmlWriter.close();
			writer.close();
			xmlWriter.close();
			fos.close();
			in.close();

		} catch (DocumentException e) {
			e.printStackTrace();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private void nodeByNodes(Element element, String spacing) {
		if (element == null) {
			return;
		}

		String sp = element.attributeValue("letter-spacing");
		spacing = sp != null ? sp : spacing;

		for (@SuppressWarnings("unchecked")
		Iterator<Object> i = element.nodeIterator(); i.hasNext();) {
			Object o = i.next();
			if (o instanceof Element) {
				Element e = (Element) o;
				nodeByNodes(e, spacing);
			} else if (spacing != null && o instanceof Text) {

				String spacingToAdd = "";

				if (spacing.equals("normal") || spacing.equals("none")) {
					spacingToAdd = "";
				
				} else if (spacing.equals("202F")) {
					spacingToAdd += '\u202F';
				
				} else if (spacing.equals("2002")) {
					spacingToAdd += '\u2002';
				
				} else if (spacing.equals("2003")) {
					spacingToAdd += '\u2003';

				} else if (spacing.equals("2004")) {
					spacingToAdd += '\u2004';

				} else if (spacing.equals("2005")) {
					spacingToAdd += '\u2005';

				} else if (spacing.equals("2006")) {
					spacingToAdd += '\u2006';

				} else if (spacing.equals("2009")) {
					spacingToAdd += '\u2009';

				} else if (spacing.equals("200A")) {
					spacingToAdd += '\u200A';

				}

				Text tNode = (Text) o;
				String text = tNode.getText();
				String newText = "";
				for (int j = 0, len = text.length(); j < len; j++) {
					newText += spacingToAdd + text.charAt(j);
				}
				newText += spacingToAdd;
				tNode.setText(newText);
			} else {
			}

		}

	}

	public void changeWithSAXParser() {
		SAXParserFactory factory = SAXParserFactory.newInstance();
		SAXParser saxParser = null;
		XmlHandler xh = new XmlHandler();

		try {
			InputStream in = new FileInputStream(filePath);
			InputSource is = new InputSource(in);
			saxParser = factory.newSAXParser();
			saxParser.parse(is, xh);
			FileOutputStream fos = new FileOutputStream(filePath);
			OutputStreamWriter out = new OutputStreamWriter(fos, "UTF-8");
			out.write(content.toString());
			out.close();
			fos.close();
			in.close();

		} catch (ParserConfigurationException e) {
			Logger.getLogger(this.getClass().getName()).error("", e);
			e.printStackTrace();
		} catch (SAXException e) {
			Logger.getLogger(this.getClass().getName()).error("", e);
			e.printStackTrace();
		} catch (IOException e) {
			Logger.getLogger(this.getClass().getName()).error("", e);
			e.printStackTrace();
		}

	}

	private class XmlHandler extends SAXContentHandler {

		public void startDocument() {

		}

		public void endDocument() {
		}

		public void startElement(String uri, String localName, String qName,
				Attributes atts) {

			String spacing = atts.getValue("letter-spacing");
			if (spacing != null && (spacing.equals("normal") || spacing.equals("none"))) //default value
				spacing = "";
			if (spacing != null) {
				sp2.add(spacing);
			}
			sp.add(spacing);
			if (qName != null) {
				String str = "<" + qName;
				for (int i = 0; i < atts.getLength(); i++) {
					str += " " + atts.getQName(i) + "=\"" + atts.getValue(i)
							+ "\"";
				}
				str += ">";
				content.append(str);
			}
		}

		public void endElement(String uri, String localName, String qName) {

			if (sp.getLast() != null) {
				sp2.removeLast();
			}
			sp.removeLast();
			if (qName != null) {
				content.append("</" + qName + ">");
			}
		}

		public void characters(char[] ch, int start, int length) {

			String data = new String(ch, start, length);
			String spacing = null;

			if (!sp2.isEmpty()) {
				spacing = sp2.getLast();
			}

			if (spacing != null && !spacing.equals("")) {
				spacing = "&#x" + spacing + ";";
				String out2 = "";
				String c;
				for (int j = 0, len = data.length(); j < len; j++) {
					c = data.charAt(j) + "";
					c = c.replace("&", "&amp;");
					c = c.replace("<", "&lt;");
					c = c.replace(">", "&gt;");
					out2 += spacing + c;
				}
				out2 += spacing + "&#x00A0;";//TODO: Distinguish between a nb space and a normal space depending on content
				data = out2;
			} else {
				data = data.replace("&", "&amp;");
				data = data.replace("<", "&lt;");
				data = data.replace(">", "&gt;");
			}
			content.append(data);

		}
	}

	public static void main(String args[]) throws FileNotFoundException {
		new SpacingHandle("d://testdatei.xml");
	}

}
